Skip to content

Conversation

ztjhz
Copy link

@ztjhz ztjhz commented Dec 12, 2024

This PR only lays out the i18n foundation (only some text have been translated to English).

Translations are organized in JSON files located at public/locales/{{lang}}/translation.json, where {{lang}} represents the language code (e.g., en, ja, es).

To support a new language:

  • Create a new directory: Inside public/locales, create a folder named with the language code of the new language. For example, for Japanese, create public/locales/ja.
  • Add the translation file: Inside the newly created language directory, add a translation.json file. This file will contain the key-value pairs for the translated strings.

Usage Examples

Inside Components:

import { useTranslation } from "react-i18next";

const Component = () => {
  const { t } = useTranslation();

  return <h1>{t('sampleKey')}</h1>;
};

Outside Components:

import i18n from './i18n'; // Assuming your i18n setup is in i18n.ts or i18n.js

const translatedText = i18n.t('sampleKey');

@ayaka14732 ayaka14732 linked an issue Dec 12, 2024 that may be closed by this pull request
Copy link
Member

@graphemecluster graphemecluster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I would prefer a library that sticks on ICU MessageFormat since the Intl.MessageFormat proposal will eventually be a part of the ES spec. What do @syimyuzya @untunt @ayaka14732 think?

Additionally, before we can move on, we will need to decide on whether:

  • to remove the English translations temporarily and merge anyway; or
  • to merge into an alternative branch, nk2028:i18n

since English users will see messages in two languages at the same time as not all messages are translated.

escapeValue: false, // not needed for react as it escapes by default
},
backend: {
loadPath: "/tshet-uinh-autoderiver/locales/{{lng}}/translation.json",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need to follow assetLocation in vite.config.ts since we also have vite.cos.config.ts for production

Comment on lines 575 to 580
document.documentElement.lang = i18n.language;
i18n.on("languageChanged", lng => {
document.documentElement.lang = lng;
});

document.title = t("tshetUinhAutoderiver");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be put into i18n.ts since nothing is related to the App component.

Suggested change
document.documentElement.lang = i18n.language;
i18n.on("languageChanged", lng => {
document.documentElement.lang = lng;
});
document.title = t("tshetUinhAutoderiver");
document.documentElement.lang = i18n.language;
document.title = i18n.t("tshetUinhAutoderiver");
i18n.on("languageChanged", lng => {
document.documentElement.lang = lng;
document.title = i18n.t("tshetUinhAutoderiver");
});

Comment on lines 198 to 199
}, []);
const onHideTooltip = useCallback(() => setCopyTooltipText("複製到剪貼簿"), []);
const onHideTooltip = useCallback(() => setCopyTooltipText(t("copyToClipboard")), []);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t will be a dependency in these cases

src/options.tsx Outdated
@@ -204,13 +205,16 @@ export const evaluateOption: Record<Option, Handler> = {
return result.length ? (
<>
<Title>
找到 {result.length} 個相異項目。
{i18n.t("schemaCompareDifferent", { count: result.length })}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is tricky, we will need a functional component like

function Translation({ message, ...options }) {
  const { t } = useTranslation();
  return t(message, options);
}

to get the messages sync with the language chosen (I am new to the i18next library and I haven’t think of how to type the component correctly yet, but I believe this should be what react-i18next provide. I wonder if there are alternative i18n libraries that provide such components.)

@ayaka14732 ayaka14732 self-assigned this Jun 19, 2025
@ayaka14732 ayaka14732 marked this pull request as draft June 19, 2025 10:47
@ayaka14732 ayaka14732 marked this pull request as ready for review June 19, 2025 19:51
@ayaka14732
Copy link
Member

Honestly, I would prefer a library that sticks on ICU MessageFormat since the Intl.MessageFormat proposal will eventually be a part of the ES spec.

That’s not really an issue. i18next is a widely used and well-supported library. ICU MessageFormat doesn’t seem particularly relevant in this context, since we’re only using formatting for one of the strings, while the rest are just straightforward string replacements.

@ayaka14732
Copy link
Member

All the strings (as far as I can test) have been translated into English.

Copy link
Member

@graphemecluster graphemecluster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t should be added to React hooks’ dependency arrays. I wonder if ESLint is checking this.
I’ll come back next month to test out the PR and give a more detailed review.

<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Noto+Serif+KR:wght@400;700&family=Noto+Serif+SC:wght@400;700&family=Noto+Serif+TC:wght@400;700&family=Jomolhari&display=swap" />
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&family=Noto+Serif+KR:wght@400;700&family=Noto+Serif+SC:wght@400;700&family=Noto+Serif+TC:wght@400;700&family=Jomolhari&display=swap" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although it doesn’t make much difference, are we actually using all the weights of the font?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we are only using Regular and Bold

Comment on lines +28 to +37
html:lang(zh-HK), html :lang(zh-HK) {
font-family: "Source Han Serif C", "Source Han Serif K", "Noto Serif CJK KR", "Source Han Serif SC",
"Noto Serif CJK SC", "Source Han Serif", "Noto Serif CJK JP", "Source Han Serif TC", "Noto Serif CJK TC",
"Noto Serif KR", "Noto Serif SC", "Noto Serif TC", "Jomolhari", "HanaMin", "CharisSILW", serif;
}
html:lang(en-GB), html :lang(en-GB) {
font-family: "Roboto", "Source Han Serif C", "Source Han Serif K", "Noto Serif CJK KR", "Source Han Serif SC",
"Noto Serif CJK SC", "Source Han Serif", "Noto Serif CJK JP", "Source Han Serif TC", "Noto Serif CJK TC",
"Noto Serif KR", "Noto Serif SC", "Noto Serif TC", "Jomolhari", "HanaMin", "CharisSILW", sans-serif;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer extracting the common part to a CSS variable

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although it doesn't make a difference, it can be simplified to

Suggested change
html:lang(zh-HK), html :lang(zh-HK) {
font-family: "Source Han Serif C", "Source Han Serif K", "Noto Serif CJK KR", "Source Han Serif SC",
"Noto Serif CJK SC", "Source Han Serif", "Noto Serif CJK JP", "Source Han Serif TC", "Noto Serif CJK TC",
"Noto Serif KR", "Noto Serif SC", "Noto Serif TC", "Jomolhari", "HanaMin", "CharisSILW", serif;
}
html:lang(en-GB), html :lang(en-GB) {
font-family: "Roboto", "Source Han Serif C", "Source Han Serif K", "Noto Serif CJK KR", "Source Han Serif SC",
"Noto Serif CJK SC", "Source Han Serif", "Noto Serif CJK JP", "Source Han Serif TC", "Noto Serif CJK TC",
"Noto Serif KR", "Noto Serif SC", "Noto Serif TC", "Jomolhari", "HanaMin", "CharisSILW", sans-serif;
}
:lang(zh-HK) {
font-family: "Source Han Serif C", "Source Han Serif K", "Noto Serif CJK KR", "Source Han Serif SC",
"Noto Serif CJK SC", "Source Han Serif", "Noto Serif CJK JP", "Source Han Serif TC", "Noto Serif CJK TC",
"Noto Serif KR", "Noto Serif SC", "Noto Serif TC", "Jomolhari", "HanaMin", "CharisSILW", serif;
}
:lang(en-GB) {
font-family: "Roboto", "Source Han Serif C", "Source Han Serif K", "Noto Serif CJK KR", "Source Han Serif SC",
"Noto Serif CJK SC", "Source Han Serif", "Noto Serif CJK JP", "Source Han Serif TC", "Noto Serif CJK TC",
"Noto Serif KR", "Noto Serif SC", "Noto Serif TC", "Jomolhari", "HanaMin", "CharisSILW", sans-serif;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Random thought: have you considered en-HK?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although it doesn't make a difference, it can be simplified to ...

IIRC the priorities of html :lang(en-GB) and :lang(en-GB) are different. Therefore, when combining with other rules, the rendering results would be different.

I need to find an example for this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Random thought: have you considered en-HK?

I guess en-GB would be better for SEO purposes?

@@ -864,7 +867,9 @@ export default function SchemaEditor({ state, setState, commonOptions, evaluateH
)}
</Parameters>
) : (
<NoParameters>此推導方案無需選項。</NoParameters>
<NoParameters>
<Trans t={t}>此推導方案無需選項。</Trans>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this line, but not the others, uses the <Trans> component?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t(...) does not work with interpolations (i.e. {{count}} in "Found {{count}} different items.")

import "purecss/build/pure.css";
import { useTranslation } from "react-i18next";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is weird, but you’ll need to swap the imports manually. ESLint doesn’t handle such edge cases.

@graphemecluster
Copy link
Member

The language dropdown is unstyled and is too large.

Screenshot of the language dropdown in Chinese
Screenshot of the language dropdown in English

@graphemecluster
Copy link
Member

@ztjhz @ayaka14732 Would you like to work on the remaining issues yourselves, or would you prefer me to take care of them?

@graphemecluster
Copy link
Member

graphemecluster commented Jul 26, 2025

TODO for myself:

TODO later (probably after merging):

  • Eliminate all t imports from i18n-next and setup ESLint rule to forbid importing i18n-next except in i18n.ts

@ayaka14732
Copy link
Member

ayaka14732 commented Aug 7, 2025

The language dropdown is unstyled and is too large.

I've realised it. I don't have an idea to make it look better 😂

Would you like to work on the remaining issues yourselves, or would you prefer me to take care of them?

@graphemecluster I think you can work on it, because I don't know how to solve some of the problems, and @ztjhz may not have time to work on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Multilingual UI
3 participants