mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-16 09:57:08 -04:00
Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
d1455b3761
89 changed files with 369 additions and 285 deletions
|
@ -19,12 +19,39 @@
|
|||
import "./discord.css";
|
||||
|
||||
import { MessageObject } from "@api/MessageEvents";
|
||||
import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
|
||||
import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
|
||||
import { Channel, Guild, Message, User } from "discord-types/general";
|
||||
import { Except } from "type-fest";
|
||||
|
||||
import { runtimeHashMessageKey } from "./intlHash";
|
||||
import { Logger } from "./Logger";
|
||||
import { MediaModalItem, MediaModalProps, openMediaModal } from "./modal";
|
||||
|
||||
const IntlManagerLogger = new Logger("IntlManager");
|
||||
|
||||
/**
|
||||
* Get an internationalized message from a non hashed key
|
||||
* @param key The plain message key
|
||||
* @param values The values to interpolate, if it's a rich message
|
||||
*/
|
||||
export function getIntlMessage(key: string, values?: Record<PropertyKey, any>): any {
|
||||
return getIntlMessageFromHash(runtimeHashMessageKey(key), values, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an internationalized message from a hashed key
|
||||
* @param hashedKey The hashed message key
|
||||
* @param values The values to interpolate, if it's a rich message
|
||||
*/
|
||||
export function getIntlMessageFromHash(hashedKey: string, values?: Record<PropertyKey, any>, originalKey?: string): any {
|
||||
try {
|
||||
return values == null ? i18n.intl.string(i18n.t[hashedKey]) : i18n.intl.format(i18n.t[hashedKey], values);
|
||||
} catch (e) {
|
||||
IntlManagerLogger.error(`Failed to get intl message for key: ${originalKey ?? hashedKey}`, e);
|
||||
return originalKey ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the invite modal
|
||||
* @param code The invite code
|
||||
|
|
|
@ -22,6 +22,7 @@ export * from "./ChangeList";
|
|||
export * from "./constants";
|
||||
export * from "./discord";
|
||||
export * from "./guards";
|
||||
export * from "./intlHash";
|
||||
export * from "./lazy";
|
||||
export * from "./lazyReact";
|
||||
export * from "./localStorage";
|
||||
|
|
52
src/utils/intlHash.ts
Normal file
52
src/utils/intlHash.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
/* eslint-disable simple-header/header */
|
||||
|
||||
/**
|
||||
* discord-intl
|
||||
*
|
||||
* @copyright 2024 Discord, Inc.
|
||||
* @link https://github.com/discord/discord-intl
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { hash as h64 } from "@intrnl/xxhash64";
|
||||
|
||||
const BASE64_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");
|
||||
const IS_BIG_ENDIAN = (() => {
|
||||
const array = new Uint8Array(4);
|
||||
const view = new Uint32Array(array.buffer);
|
||||
return !((view[0] = 1) & array[0]);
|
||||
})();
|
||||
|
||||
function numberToBytes(number: number | bigint) {
|
||||
number = BigInt(number);
|
||||
const array: number[] = [];
|
||||
const byteCount = Math.ceil(Math.floor(Math.log2(Number(number)) + 1) / 8);
|
||||
for (let i = 0; i < byteCount; i++) {
|
||||
array.unshift(Number((number >> BigInt(8 * i)) & BigInt(255)));
|
||||
}
|
||||
|
||||
const bytes = new Uint8Array(array);
|
||||
// The native `hashToMessageKey` always works in Big/Network Endian bytes, so this array
|
||||
// needs to be converted to the same endianness to get the same base64 result.
|
||||
return IS_BIG_ENDIAN ? bytes : bytes.reverse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a consistent, short hash of the given key by first processing it through a hash digest,
|
||||
* then encoding the first few bytes to base64.
|
||||
*
|
||||
* This function is specifically written to mirror the native backend hashing function used by
|
||||
* `@discord/intl-loader-core`, to be able to hash names at runtime.
|
||||
*/
|
||||
export function runtimeHashMessageKey(key: string): string {
|
||||
const hash = h64(key, 0);
|
||||
const bytes = numberToBytes(hash);
|
||||
return [
|
||||
BASE64_TABLE[bytes[0] >> 2],
|
||||
BASE64_TABLE[((bytes[0] & 0x03) << 4) | (bytes[1] >> 4)],
|
||||
BASE64_TABLE[((bytes[1] & 0x0f) << 2) | (bytes[2] >> 6)],
|
||||
BASE64_TABLE[bytes[2] & 0x3f],
|
||||
BASE64_TABLE[bytes[3] >> 2],
|
||||
BASE64_TABLE[((bytes[3] & 0x03) << 4) | (bytes[3] >> 4)],
|
||||
].join("");
|
||||
}
|
|
@ -16,12 +16,31 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { runtimeHashMessageKey } from "./intlHash";
|
||||
import { Patch, PatchReplacement, ReplaceFn } from "./types";
|
||||
|
||||
export function canonicalizeMatch<T extends RegExp | string>(match: T): T {
|
||||
if (typeof match === "string") return match;
|
||||
const canonSource = match.source
|
||||
.replaceAll("\\i", "[A-Za-z_$][\\w$]*");
|
||||
let partialCanon = typeof match === "string" ? match : match.source;
|
||||
partialCanon = partialCanon.replaceAll(/#{intl::([A-Za-z_$][\w$]*)}/g, (_, key) => {
|
||||
const hashed = runtimeHashMessageKey(key);
|
||||
|
||||
const isString = typeof match === "string";
|
||||
const hasSpecialChars = !Number.isNaN(Number(hashed[0])) || hashed.includes("+") || hashed.includes("/");
|
||||
|
||||
if (hasSpecialChars) {
|
||||
return isString
|
||||
? `["${hashed}"]`
|
||||
: String.raw`(?:\["${hashed}"\])`.replaceAll("+", "\\+");
|
||||
}
|
||||
|
||||
return isString ? `.${hashed}` : String.raw`(?:\.${hashed})`;
|
||||
});
|
||||
|
||||
if (typeof match === "string") {
|
||||
return partialCanon as T;
|
||||
}
|
||||
|
||||
const canonSource = partialCanon.replaceAll(String.raw`\i`, String.raw`(?:[A-Za-z_$][\w$]*)`);
|
||||
return new RegExp(canonSource, match.flags) as T;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue