-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6 from Explicit12/Implement-internationalization-…
…feature Implement internationalization feature
- Loading branch information
Showing
15 changed files
with
257 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { createLocale, LocaleSymbol } from "./src/composable.locale"; | ||
|
||
export { default as createVueI18nAdapter } from "./src/adatper.locale/vue-i18n"; | ||
export { default as defaultEnLocale } from "./src/adatper.locale/locales/en"; | ||
|
||
export function createVueless(options = {}) { | ||
const locale = createLocale(options.locale); | ||
|
||
const install = (app) => { | ||
app.provide(LocaleSymbol, locale); | ||
}; | ||
|
||
return { | ||
install, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import selectConfig from "../../ui.form-select/configs/default.config"; | ||
import switchConfig from "../../ui.form-switch/configs/default.config"; | ||
import inputFileConfig from "../../ui.form-input-file/configs/default.config"; | ||
import dropdownListConfig from "../../ui.dropdown-list/configs/default.config"; | ||
import modalConfirmConfig from "../../ui.container-modal-confirm/configs/default.config"; | ||
import tableConfig from "../../ui.data-table/configs/default.config"; | ||
import calendarConfig from "../../ui.form-calendar/configs/default.config"; | ||
import datepickerConfig from "../../ui.form-date-picker/configs/default.config"; | ||
import datepickerRangeConfig from "../../ui.form-date-picker-range/configs/default.config"; | ||
|
||
export default { | ||
USelect: selectConfig.i18n, | ||
USwitch: switchConfig.i18n, | ||
UInputFile: inputFileConfig.i18n, | ||
UDropdownList: dropdownListConfig.i18n, | ||
UModalConfirm: modalConfirmConfig.i18n, | ||
UTable: tableConfig.i18n, | ||
UCalendar: calendarConfig.i18n, | ||
UDatePicker: datepickerConfig.i18n, | ||
UDatePickerRange: datepickerRangeConfig.i18n, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export default function createVueI18nAdapter(i18n) { | ||
return { | ||
name: "vue-i18n", | ||
locale: i18n.global.locale, | ||
fallback: i18n.global.fallbackLocale, | ||
messages: i18n.global.messages, | ||
t: (key, ...params) => i18n.global.t(key, params), | ||
tm: i18n.global.tm, | ||
n: i18n.global.n, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { shallowRef, ref } from "vue"; | ||
|
||
import en from "./locales/en"; | ||
|
||
const FALLBACK_LOCALE_CODE = "en"; | ||
|
||
export default function createVuelessAdapter(options) { | ||
const current = shallowRef(options?.locale ?? FALLBACK_LOCALE_CODE); | ||
const fallback = shallowRef(options?.fallback ?? FALLBACK_LOCALE_CODE); | ||
|
||
const messages = ref({ en, ...options?.messages }); | ||
|
||
return { | ||
name: "vueless", | ||
locale: current, | ||
fallback, | ||
messages, | ||
t: createTranslateFunction(current, fallback, messages), | ||
tm: createTranslateMessageFunction(current, fallback, messages), | ||
n: createNumberFunction(current, fallback), | ||
}; | ||
} | ||
|
||
function createTranslateFunction(current, fallback, messages) { | ||
return (key, ...params) => { | ||
const currentLocale = current.value && messages.value[current.value]; | ||
const fallbackLocale = fallback.value && messages.value[fallback.value]; | ||
|
||
let str = getObjectValueByPath(currentLocale, key, null); | ||
|
||
if (!str) { | ||
// eslint-disable-next-line no-console | ||
console.warn( | ||
`Translation key "${key}" not found in "${current.value}", trying fallback locale`, | ||
); | ||
str = getObjectValueByPath(fallbackLocale, key, null); | ||
} | ||
|
||
if (!str) { | ||
// eslint-disable-next-line no-console | ||
console.warn(`Translation key "${key}" not found in fallback`); | ||
str = key; | ||
} | ||
|
||
if (typeof str !== "string") { | ||
// eslint-disable-next-line no-console | ||
console.warn(`Translation key "${key}" has a non-string value`); | ||
str = key; | ||
} | ||
|
||
return replace(str, params); | ||
}; | ||
} | ||
|
||
function createTranslateMessageFunction(current, fallback, messages) { | ||
return (key) => { | ||
const currentLocale = current.value && messages.value[current.value]; | ||
const fallbackLocale = fallback.value && messages.value[fallback.value]; | ||
|
||
let str = getObjectValueByPath(currentLocale, key, null); | ||
|
||
if (str === undefined) { | ||
// eslint-disable-next-line no-console | ||
console.warn( | ||
`Translation key "${key}" not found in "${current.value}", trying fallback locale`, | ||
); | ||
str = getObjectValueByPath(fallbackLocale, key, null); | ||
} | ||
|
||
return str; | ||
}; | ||
} | ||
|
||
const replace = (str, params) => { | ||
return str.replace(/\{(\d+)\}/g, (match, index) => { | ||
return String(params[+index]); | ||
}); | ||
}; | ||
|
||
function createNumberFunction(current, fallback) { | ||
return (value, options) => { | ||
const numberFormat = new Intl.NumberFormat([current.value, fallback.value], options); | ||
|
||
return numberFormat.format(value); | ||
}; | ||
} | ||
|
||
export function getObjectValueByPath(obj, path, fallback) { | ||
if (obj == null || !path || typeof path !== "string") return fallback; | ||
if (obj[path] !== undefined) return obj[path]; | ||
path = path.replace(/\[(\w+)\]/g, ".$1"); // convert indexes to properties | ||
path = path.replace(/^\./, ""); // strip a leading dot | ||
|
||
return getNestedValue(obj, path.split("."), fallback); | ||
} | ||
|
||
export function getNestedValue(obj, path, fallback) { | ||
const last = path.length - 1; | ||
|
||
if (last < 0) { | ||
return obj === undefined ? fallback : obj; | ||
} | ||
|
||
for (let i = 0; i < last; i++) { | ||
if (obj == null) { | ||
return fallback; | ||
} | ||
|
||
obj = obj[path[i]]; | ||
} | ||
|
||
if (obj == null) { | ||
return fallback; | ||
} | ||
|
||
return obj[path[last]] === undefined ? fallback : obj[path[last]]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { inject } from "vue"; | ||
import createVuelessAdapter from "../adatper.locale/vueless"; | ||
|
||
export const LocaleSymbol = Symbol.for("vueless:locale"); | ||
|
||
function isLocaleInstance(obj) { | ||
return obj.name !== null; | ||
} | ||
|
||
export function createLocale(options) { | ||
const i18n = | ||
options?.adapter && isLocaleInstance(options?.adapter) | ||
? options?.adapter | ||
: createVuelessAdapter(options); | ||
|
||
return { ...i18n }; | ||
} | ||
|
||
export function useLocale() { | ||
const locale = inject(LocaleSymbol); | ||
|
||
if (!locale) throw new Error("[vueless] Could not find injected locale instance"); | ||
|
||
return locale; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.